<script lang="tsx">
import { TableActionType } from "@/components/Table";
import { useI18n } from "@/hooks/web/useI18n";
import { computed, defineComponent, PropType, Ref, toRefs, unref } from "vue";
import componentMap from "../componentMap";
import { FormActionType, FormProps, FormSchema } from "../types/form";
import { Recordable } from "/#/global";
import { ElCol, ElForm, ElDivider, FormItemRule } from 'element-plus'
import { getSlot } from "@/utils/helper/tsxHelper";
import { isArray, isBoolean, isEmpty, isFunction, isNull, isNumber, isObject } from "@/utils/is";
import { BasicHelp } from '@/components/Basic';
import { cloneDeep, merge, upperFirst } from "lodash-es";
import { InternalRuleItem, SyncValidateResult, ValidateOption, Value, Values } from "async-validator";
import { selectType, setComponentRuleType } from "../helper";
import { treeToList } from "@/utils/helper/treeHelper";


export default defineComponent({
  name: 'BasicFormItem',
  inheritAttrs: false,
  props: {
    schema: {
      type: Object as PropType<FormSchema>,
      default: () => ({}),
    },
    formProps: {
      type: Object as PropType<FormProps>,
      default: () => ({}),
    },
    allDefaultValues: {
      type: Object as PropType<Recordable>,
      default: () => ({}),
    },
    formModel: {
      type: Object as PropType<Recordable>,
      default: () => ({}),
    },
    setFormModel: {
      type: Function as PropType<(key: string, value: any) => void>,
      default: null,
    },
    tableAction: {
      type: Object as PropType<TableActionType>,
    },
    formActionType: {
      type: Object as PropType<FormActionType>,
    },
  },
  setup(props, { slots }) {
    const { t } = useI18n();

    const { schema, formProps } = toRefs(props) as {
      schema: Ref<FormSchema>;
      formProps: Ref<FormProps>;
    };

    const getValues = computed(() => {
      const { allDefaultValues, formModel, schema } = props;
      const { mergeDynamicData } = props.formProps;
      return {
        field: schema.field,
        model: formModel,
        values: {
          ...mergeDynamicData,
          ...allDefaultValues,
          ...formModel,
        } as Recordable,
        schema: schema,
      };
    })

    const getComponentsProps = computed(() => {
      const { schema, tableAction, formModel, formActionType } = props;
      let { componentProps = {} } = schema;
      if (isFunction(componentProps)) {
        componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
      }
      if (schema.component === 'ElDivider') {
        componentProps = Object.assign({ direction: 'horizontal' }, componentProps, {
          contentPosition: 'left',
          plain: true,
        });
      }
      return componentProps as Recordable;
    });

    function renderLabelHelpMessage() {
      const { label, helpMessage, helpComponentProps, subLabel } = props.schema;
      const renderLabel = subLabel ? (
        <span>
          {label} <span class="text-secondary">{subLabel}</span>
        </span>
      ) : (
        label
      );
      const getHelpMessage = isFunction(helpMessage)
        ? helpMessage(unref(getValues))
        : helpMessage;
      if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) {
        return renderLabel;
      }
      return (
        <span>
          <span class="mr-1"> {renderLabel}</span>
          <BasicHelp placement="top" class="mx-1" text={getHelpMessage} {...helpComponentProps} />
        </span>
      );
    }

    function getFormModelValue(fieldKey: string, formModelValue: any): number | string | Array<string | number> {
      const fieldKeys = fieldKey?.split(".")
      let values: Array<string | number> = []

      if (isArray(formModelValue)) {
        if (!isArray(formModelValue[0]) && !isObject(formModelValue[0])) {
          formModelValue.forEach((item) => {
            values.push(item)
          })
          return values
        }

        formModelValue.forEach(item => {
          fieldKeys.forEach((key, index) => {
            if (isObject(item[key]) || isObject(item[key])) {
              getFormModelValue(fieldKey.slice(index, 1), item[key])
            } else {
              values.push(item[key])
            }
          })

        })
      } else if (isObject(formModelValue)) {
        fieldKeys.forEach((key, index) => {
          if (isObject(formModelValue[key])) {
            getFormModelValue(fieldKey.slice(index, 1), formModelValue[key])
          } else {
            values.push(formModelValue[key])
          }
        })
      } else {
        values.push(formModelValue)
      }
      return values === [] ? '' : values
    }

    function renderComponent() {
      const {
        renderComponentContent,
        component,
        field,
        fieldKey,
        changeEvent = 'update:modelValue',
        valueField,
        componentProps,
        defaultValue = '',
        slots
      } = props.schema;

      console.log(field)
      console.log( props.formModel)

      let formModelValue: any = props.formModel[field]
      // console.log(props.formModel[field])

      //  解析对象或者集合
      if (formModelValue && (isObject(formModelValue) || isArray(formModelValue)) && fieldKey) {
        formModelValue = getFormModelValue(fieldKey, formModelValue)
      }

      // console.log(formModelValue)

      const eventKey = `on${upperFirst(changeEvent)}`;

      const on = {
        [eventKey]: (args: [] | string | Recordable | Recordable[]) => {
          console.log('值改变')
          console.log(args)
          if (propsData[eventKey]) {
            if (isArray(args)) {
              propsData[eventKey](...args);
            }
          }

          console.log(component)
          console.log(isArray(args))

          // 把选择的id转成对象
          let values: [] | string | Recordable | Recordable = []
          if (selectType.includes(component) && isArray(args)) {
            console.log('转换')
            // 树节点需要转换成平级
            let data = unref(getComponentsProps).options || unref(getComponentsProps).data
            data = treeToList(data)
            console.log(data)
            if (componentProps && data) {
              values = data.filter((option: never) => args.includes(option['id']))
              console.log(values)
            }
          } else {
            values = args
          }

          props.setFormModel(field, values);

        }
      }


      const isCheck = component && ['ElSwitch', 'ElCheckbox'].includes(component);

      const bindValue: Recordable = {
        [valueField || (isCheck ? 'checked' : 'modelValue')]: (formModelValue ? formModelValue : defaultValue)
      }

      if (component && ['ElInputNumber'].includes(component)  && defaultValue) {
        bindValue.modelValue = Number(bindValue.modelValue)
      }
      console.log(formModelValue)
      console.log(bindValue)

      const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;

      const { size } = props.formProps;

      let propsData: Recordable = {
        size,
        clearable: true,
      }
      propsData.prop = field;
      if (isArray(formModelValue)) {
        propsData.multiple = true
      }

      if (componentProps && isObject(componentProps)) {
        propsData = merge(propsData, componentProps)
      }


      let componentMinWidth = props.formProps['componentMinWidth']
      if (isNumber(componentMinWidth)) {
        componentMinWidth = componentMinWidth + 'px'
      }

      const style = {
        'width': propsData['width'] ? propsData['width'] : componentMinWidth,
        ...propsData['style']
      }

      const compAttr: Recordable = {
        ...bindValue,
        style,
        ...on,
        ...propsData
      }

      if (!renderComponentContent) {
        console.log(compAttr)
        return <Comp {...compAttr} v-slots={slots} />;
      }
      //  自定义组件内容
      const compSlot = isFunction(renderComponentContent)
        ? { default: renderComponentContent(unref(getValues)) }
        : {
          default: () => renderComponentContent,
        };

      return <Comp {...compAttr}>{compSlot}
      </Comp>;
    }

    function handleRules(): FormItemRule[] {
      const { rules: defRules = [], dynamicRules, required, component } = props.schema


      if (isFunction(dynamicRules)) {
        return dynamicRules(unref(getValues)) as FormItemRule[];
      }

      let rules: FormItemRule[] = cloneDeep(defRules) as FormItemRule[];

      //  如果字段必填但是没有规则就使用默认的验证
      const getRequired = isFunction(required) ? required(unref(getValues)) : required;

      const defaultMsg = '默认消息'

      function validator(rule: InternalRuleItem, value: Value, callback?: (error?: string | Error) => void, source?: Values, options?: ValidateOption): SyncValidateResult | void {
        const msg = rule.message as string || defaultMsg;
        if (value === undefined || isNull(value)) {
          // 空值
          return msg;
        } else if (Array.isArray(value) && value.length === 0) {
          // 数组类型
          return msg;
        } else if (typeof value === 'string' && value.trim() === '') {
          // 空字符串
          return msg;
        } else if (
          typeof value === 'object' &&
          Reflect.has(value, 'checked') &&
          Reflect.has(value, 'halfChecked') &&
          Array.isArray(value.checked) &&
          Array.isArray(value.halfChecked) &&
          value.checked.length === 0 &&
          value.halfChecked.length === 0
        ) {
          // 非关联选择的tree组件
          return msg;
        }
      }

      // 默认规则验证
      if ((!rules || rules.length === 0) && getRequired) {
        rules = [{ required: getRequired, validator: validator }];
      }

      //  必填规则索引检查
      const requiredRuleIndex: number = rules.findIndex(
        (rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator'),
      );
      if (requiredRuleIndex !== -1) {
        const rule = rules[requiredRuleIndex];
        if (component) {
          if (Reflect.has(rule, 'type')) {
            rule.type = component === 'ElInputNumber' ? 'number' : rule.type ? rule.type : 'string'
          }

          rule.message = rule.message || defaultMsg;

          if (component.includes('ElInput') || component.includes('textarea')) {
            rule.whitespace = true;
          }
          const valueFormat = unref(getComponentsProps)?.valueFormat;

          setComponentRuleType(rule, component, valueFormat)
        }
      }



      // Maximum input length rule check
      const characterInx = rules.findIndex((val) => val.max);
      if (characterInx !== -1 && !rules[characterInx].validator) {
        rules[characterInx].message =
          rules[characterInx].message ||
          t('component.form.maxTip', [rules[characterInx].max] as Recordable);
      }

      return rules
    }

    function getShow(): { isShow: boolean; isIfShow: boolean } {
      const { show, ifShow } = props.schema;
      const { showAdvancedButton } = props.formProps;
      const itemIsAdvanced = showAdvancedButton
        ? isBoolean(props.schema.isAdvanced)
          ? props.schema.isAdvanced
          : true
        : true;

      let isShow = true;
      let isIfShow = true;

      if (isBoolean(show)) {
        isShow = show;
      }
      if (isBoolean(ifShow)) {
        isIfShow = ifShow;
      }
      if (isFunction(show)) {
        isShow = show(unref(getValues));
      }
      if (isFunction(ifShow)) {
        isIfShow = ifShow(unref(getValues));
      }
      isShow = isShow && itemIsAdvanced;

      return { isShow, isIfShow };
    }

    function renderItem() {
      const { itemProps, slot, render, field, suffix, component } = props.schema;

      if (component === 'ElDivider') {
        return (
          <ElCol gutter={24}>
            <ElDivider {...unref(getComponentsProps)}>{renderLabelHelpMessage()}</ElDivider>
          </ElCol>
        );
      } else {
        const getContent = () => {
          return slot
            ? getSlot(slots, slot, unref(getValues))
            : render
              ? render(unref(getValues))
              : renderComponent();
        };

        const showSuffix = !!suffix;
        const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;


        return (<ElForm.FormItem class="mr-5 align-center"
          prop={field}
          rules={handleRules()}
          v-slots={{
            label: () => <div> {renderLabelHelpMessage()} </div>
          }}>
          <div style="display:flex">
            <div style="flex:1;">{getContent()}</div>
            {showSuffix && <span class="suffix">{getSuffix}</span>}
          </div>
        </ElForm.FormItem>)
      }
    }

    return () => {
      const { colProps = {}, colSlot, renderColContent, component } = props.schema;
      if (!componentMap.has(component)) {
        return null;
      }
      const { isIfShow, isShow } = getShow();

      const values = unref(getValues);

      const getContent = () => {
        return colSlot ? getSlot(slots, colSlot, values) : renderColContent
          ? renderColContent(values) : renderItem();
      };


      return isIfShow && (<ElCol v-show={isShow}>
        {getContent}
      </ElCol>)

    }
  }
})

</script>